﻿#include <QDebug>
#include "JZCoContext.h"
#include "JZCoCoroutine.h"

#ifdef _WIN32 

#include <Windows.h>

//JZMainContext
class JZCoMainContext
{
public:
    JZCoMainContext()
    {
        _ctx = ConvertThreadToFiber(NULL);
    }

    ~JZCoMainContext()
    {
        ConvertFiberToThread();
    }

    void *_ctx;
};

JZCoMainContext *createMainContext()
{
    JZCoMainContext *ctx = new JZCoMainContext();
    return ctx;
}

void destoryMainContext(JZCoMainContext *ctx)
{
    delete ctx;
}

//JZCoContext
VOID CALLBACK FiberFunction(LPVOID lpFiberParameter);
class JZCoContext
{
public:
    JZCoContext()
    {
        task = nullptr;

        SIZE_T commit_size = 128 * 1024;
        ctx_ = CreateFiberEx(commit_size, commit_size, FIBER_FLAG_FLOAT_SWITCH, (LPFIBER_START_ROUTINE)FiberFunction, (LPVOID)this);
        Q_ASSERT(ctx_);
    }
    ~JZCoContext()
    {
        DeleteFiber(ctx_);
    }

    JZCoCoroutine *task;
    void *ctx_;
};

JZCoContext* createContext(JZCoCoroutine *co)
{
    JZCoContext *co_ctx = new JZCoContext();    
    co_ctx->task = co;
    return co_ctx;
}

void destoryContext(JZCoContext *ctx)
{
    delete ctx;
}

void yieldContext(JZCoContext *ctx)
{
    void *main_ctx = g_scheduler->context()->_ctx;
    SwitchToFiber(main_ctx);
}

void resumeContext(JZCoContext *ctx)
{
    SwitchToFiber(ctx->ctx_);
}

// 纤程执行的函数
VOID CALLBACK FiberFunction(LPVOID lpFiberParameter)
{
    JZCoContext *ctx = (JZCoContext *)lpFiberParameter;
    ctx->task->run();
    g_scheduler->endTask(ctx->task);
}

#else

#include <ucontext.h>

void coroutine_function(void *ctx);

class JZCoMainContext
{
public:    
    JZCoMainContext()
    {
        getcontext(&ctx);
    }
    
    ucontext_t ctx;
};

class JZCoContext
{
public:    
    JZCoContext()
    {
        ucontext_t *main_ctx = &g_scheduler->context()->ctx;
        getcontext(&ctx);
        
        ctx.uc_stack.ss_sp = stack;
        ctx.uc_stack.ss_size = sizeof(stack);
        ctx.uc_link = main_ctx;
        makecontext(&ctx, (void (*)(void))coroutine_function, 1, this);
    }

    ucontext_t ctx;
    JZCoCoroutine *task;
    char stack[128 * 1024];
};


JZCoMainContext *createMainContext()
{
    JZCoMainContext *main_ctx = new JZCoMainContext();
    return main_ctx;
}

void destoryMainContext(JZCoMainContext *ctx)
{
    delete ctx;
}

JZCoContext* createContext(JZCoCoroutine *co)
{    
    JZCoContext *co_ctx = new JZCoContext();
    co_ctx->task = co;
    return co_ctx;
}

void destoryContext(JZCoContext *ctx)
{
    delete ctx;
}

void yieldContext(JZCoContext *ctx)
{
    ucontext_t *main_ctx = &g_scheduler->context()->ctx;
    swapcontext(&ctx->ctx,main_ctx);
}

void resumeContext(JZCoContext *ctx)
{
    ucontext_t *main_ctx = &g_scheduler->context()->ctx;
    swapcontext(main_ctx, &ctx->ctx);
}

void coroutine_function(void *ptr)
{
    JZCoContext *ctx = (JZCoContext*)ptr;
    ctx->task->run();
    g_scheduler->endTask(ctx->task);
}

#endif